package hotel.quartz.oversea.job.meituan;

import hotel.base.oversea.constant.CommonConstants;
import hotel.base.oversea.constant.CommonUrlConstants;
import hotel.base.oversea.constant.MeituanConstants;
import hotel.base.oversea.constant.enums.MessageEnums;
import hotel.base.oversea.constant.quartz.UrlMtEnums;
import hotel.base.oversea.dto.switchs.SwitchCreateRoomDto;
import hotel.base.oversea.utils.*;
import hotel.quartz.oversea.config.service.RedisService;
import hotel.quartz.oversea.job.base.BaseJob;
import hotel.quartz.oversea.util.JobRestTemplateUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  每2小时校验静态信息定时器： 1、产品：失效  （日常每两小时主要处理失效产品，push到队列）（后续每半小时定时器会pop处理）
 *                              2、酒店是否下线：当前酒店详情接口返回空时，判断为下线（并直接推送给携程）
 *                              2、程序内部判断 定点凌晨02时，内部校验包含 保存酒店信息到队列（供后续’每天一次处理静态保存到库表定时器’处理数据）--》凌晨时间点可以缓存动态设置
 *                                       pop酒店时，程序内部会同时判断处理物理房型是否失效和新增，并且将物理房型最新数据更新到库表，并且有失效和新增的会push到队列（每半小时定时器会处理）
 *                              3、程序内部判断 定点凌晨02时，内部校验包含 保存产品信息到队列（供后续’每天一次处理静态保存到库表定时器‘处理数据）--》凌晨时间点可以缓存动态设置
 *                              3、程序内部判断 定点凌晨02时，内部校验包含 判断新增产品push到队列（供后续’每天一次处理静态保存到库表定时器‘处理数据）--》凌晨时间点可以缓存动态设置
 * </p>
 *
 * @description: Basic Mt Goods Info Check Update Job
 * @author: qiuyy
 * @date: 2020-05-26 13:22
 * @version: V1.0
 */
@Slf4j
public class BasicMtGoodsInfoCheckUpdateJob implements BaseJob {
    private String timestamp = "Bmgicuj"+String.valueOf(System.currentTimeMillis())+ StringUtil.getFourRandom();
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisService redisService;

    private void setTimestamp(String timestamp){
        this.timestamp = timestamp;
    }

    /**
     * 每两小时  校验一次上架产品：1、是否失效  2.是否新增  3、是否房型id变更  4、（特定时间点判断一次所有产品静态信息早餐等的变化，pop定时器进行每天一次产品数据库信息更新）
     * @param jobExecutionContext
     */
    @Override
    @SneakyThrows
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("Basic Mt Goods Info Check Update Job {} >>>>>>>> 静态信息更新定时器 执行时间: {}", timestamp, DateUtil.getCurrTime());
        String todoBasic = null;
        String redisHour = (String)redisService.get(MeituanConstants.REDIS_BASCI_GOODS_HOTEL_CHECK_HOUR);
        if(null == redisHour){
            redisHour = "01";
        }
        String nowHH = DateUtil.formatDate(new Date(),"HH");//获取当前时间的小时数
        if(redisHour.contains(nowHH)){//redisHour可能是  "17,18"  不能是02  和pop定时器冲突
            //凌晨一点时，当前校验静态产品，内部方法将包含校验早餐信息，新增信息，和保存产品信息到队列
            todoBasic = "updateBasicDayOneCount";
            setTimestamp(timestamp+todoBasic);
            SendUtils.dingMsgNotLog("[CHECK-HOTEL-AND-GOODS]校验静态信息，识别到一次全量更新 ", DateUtil.getCurrTime()+" 程序内部将处理'酒店和产品和房型'push到队列等待定点执行pop保存到库表 ", MessageEnums.genre.GOODS_STATUS_AND_PRICE.getCode(),restTemplate);
        }

        //从缓存中获取上架的酒店产品
        Long allNeedCheckHotels = redisTemplate.opsForList().size(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS);

        Integer saleRoomNum = 0;
        if(allNeedCheckHotels==0){
            log.info("Basic Mt Goods Info Check Update Job  {} allNeedCheckHotels  当前队列size=0，重新获取缓存上架酒店，首轮重新跑批",timestamp, DateUtil.getCurrTime());
            //重置该表示待查询的酒店ids,,opsForList() 每拿出一个hotelID就会集合中剔除一个
            List<SwitchCreateRoomDto> swUpHotelIdGodosData = findUpHotelGoodsData();
            if(null != swUpHotelIdGodosData && swUpHotelIdGodosData.size()>0){
                saleRoomNum = swUpHotelIdGodosData.size();
                //构建数据
                Map<String,Map<String,String>> swMap = new HashMap<>();
                for (SwitchCreateRoomDto dto:swUpHotelIdGodosData){
                    String mtHotelId = String.valueOf(dto.getMtHotelId());
                    String goodsId = String.valueOf(dto.getMtGoodId());
                    Map<String,String> goodsMap = swMap.get(mtHotelId);
                    if(null == goodsMap || goodsMap.size()==0){
                        goodsMap = new HashMap<>();
                        log.info("待查询酒店mtHotelId={}",mtHotelId);
                    }else{
                        //log.info("goodsMapgoodsMapgoodsMap:"+goodsMap.size());
                    }
                    goodsMap.put(goodsId,dto.getMtRoomId());
                    swMap.put(String.valueOf(mtHotelId),goodsMap);
                }
                for (Map.Entry m:swMap.entrySet()){
                    Map<String,Map<String,String>> one  = new HashMap<>();
                    one.put((String)m.getKey(),(Map<String,String>)m.getValue());
                    redisTemplate.opsForList().leftPush(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS, GsonUtils.GsonString(one));
                }
            }else{
                log.error("Basic Mt Goods Info Check Update Job {} 静态产品信息更新定时器,未查询到上架酒店信息",timestamp);
            }
        }else{
            try {
                Thread.sleep(10000);
            } catch (Exception e2) {
                e2.printStackTrace();
            }
            Long newAllNeedCheckHotels = redisService.queueSize(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS);
            if(BigDecimal.valueOf(newAllNeedCheckHotels).compareTo(BigDecimal.valueOf(allNeedCheckHotels))==0){
                SendUtils.dingMsgNotLog("[CHECK-GOODS-SW]", DateUtil.getCurrTime()+" 程序判断队列大于0,无执行程序,继续执行校验静态信息", MessageEnums.genre.GOODS_STATUS_AND_PRICE.getCode(),restTemplate);
            }else{
                SendUtils.dingMsgNotLog("[CHECK-GOODS-SW]", DateUtil.getCurrTime()+" 上一个程序还在执行中，当前批次不执行处理校验静态信息", MessageEnums.genre.GOODS_STATUS_AND_PRICE.getCode(),restTemplate);
                return;
            }
        }
        Long allNeedCheckHotelsNew = redisTemplate.opsForList().size(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS);
        /*if(null == redisService.get("BasicMtGoodsInfoCheckUpdateJob_notFind")){
            if(allNeedCheckHotelsNew<10){
                SendUtils.dingMsgNotLog("[NOT-FIND-HOTELS] 查询上架酒店产品异常，请核实 ",DateUtil.getCurrTime()+" 查询到数量为："+allNeedCheckHotelsNew,MessageEnums.genre.GOODS_STATUS_AND_PRICE.getCode(),restTemplate);
            }
        }*/
        log.info("Basic Mt Goods Info Check Update Job {} 当前获取到 待查询酒店产品 allNeedCheckHotelsNew：{}",timestamp,allNeedCheckHotelsNew);
        if(allNeedCheckHotelsNew>0){
            SendUtils.dingMsgNotLog("[JOB-CHECK] 即将执行，美团静态信息校验 ", DateUtil.getCurrTime()+" 比对失效新增， 预计耗时："+allNeedCheckHotelsNew/10*(1.5)/60+" 分钟", MessageEnums.genre.GOODS_STATUS_AND_PRICE.getCode(),restTemplate);
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            Integer hotelNum = 10;//因为限流等问题 20改成10
            if(null != redisService.get("sw_check_hotels_num")){
                hotelNum = (Integer) redisService.get("sw_check_hotels_num");
            }
            Long countTask = allNeedCheckHotelsNew/hotelNum+100;
            log.info("allNeedCheckHotelsNew countTask={}",countTask);
            Integer sleepTime = 4000;//内部一次取20家酒店 分两次调用接口
            for (int i = 0; i < countTask; i++) {
                if(null != redisTemplate.opsForValue().get(MeituanConstants.REDIS_KEY_CHECK_BASIC_JOB_THREAD_STOP)){
                    log.info("主动查询停止多线程静态信息更新定时器");
                    redisService.remove("BasicMtGoodsInfoCheckUpdateJob_doing");
                    break;
                }
                log.info("队列数量：{}",redisTemplate.opsForList().size(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS));
                if(redisTemplate.opsForList().size(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS)<=10){
                    request(saleRoomNum+"_"+allNeedCheckHotelsNew+"_lastTask");
                }else{
                    request(null);
                }

                if(redisTemplate.opsForList().size(MeituanConstants.REDIS_BASIC_GOODS_INFO_QUEUE_HOTELS)==0){
                    log.info("Basic Mt Goods Info Check Update Job 缓存的酒店为0 break");
                    break;
                }
                if(null != redisService.get("BasicMtGoodsInfoCheckUpdateJob_sleep")){
                    sleepTime = (Integer) redisService.get("BasicMtGoodsInfoCheckUpdateJob_sleep");
                }
                log.info("Basic Mt Goods Info Check Update Job 睡眠 sleepTime={}",sleepTime);
                try {
                    Thread.sleep(sleepTime);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }

            }
        }
        redisService.remove("BasicMtGoodsInfoCheckUpdateJob_doing");
        log.info("Basic Mt Goods Info Check Update Job {} >>>>>>>> 静态信息更新定时器 结束时间: {},{}",timestamp, DateUtil.getCurrTime());

    }

    private List<SwitchCreateRoomDto> findUpHotelGoodsData() {
        //获取上架的酒店+产品信息
        Result result = JobRestTemplateUtils.post(CommonUrlConstants.SWITCHS_ONLINE_HOST+"/switchHotelUp/getUpHotelGoodsDataForBasicRoom",null,restTemplate);
        List<SwitchCreateRoomDto> upList = GsonUtils.jsonToList(result.getData().toString(), SwitchCreateRoomDto.class);
        if(null != upList){
            log.info("findUpHotelGoodsData获取到上架酒店产品数量size={}",upList.size());
        }else{
            log.error("异常  findUpHotelGoodsData获取到上架酒店产品数量size={}",upList.size());
        }
        return upList;
    }

    /**
     * 调用美团接口    主动查询上送的酒店是否存在变动数据，有则接口中操作存入缓存队列
     *
     * @return
     */
    private void request(String ti) {
        String host = CommonUrlConstants.MEITUAN_HOST;
        String url = host+ UrlMtEnums.findUpdateGoods.FIND.getValue();
        Map<String,String> map = new HashMap<>();
        if(!ObjectUtils.isEmpty(ti)){
            timestamp = ti+timestamp;
        }
        map.put("timestamp",timestamp);
        Result response = JobRestTemplateUtils.post(url, GsonUtils.GsonString(map),restTemplate);
        if(response.getCode()!= CommonConstants.SUCCESS){
            log.error("静态信息定时器 调用美团系统接口失败："+ GsonUtils.GsonString(response));
        }
    }

}
